package com.leyou.item.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.constant.MQConstants;
import com.leyou.common.exception.LyException;
import com.leyou.common.exception.enums.ExceptionEnum;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.item.dto.BrandDTO;
import com.leyou.item.dto.CategoryDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.entity.*;
import com.leyou.item.mapper.SkuMapper;
import com.leyou.item.mapper.SpuDetailMapper;
import com.leyou.item.mapper.SpuMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author LT
 * @version 1.0.0
 * @ClassName GoodsService.java
 * @Description TODO Goods商品的业务层
 * @createTime 2020年05月06日 23:35:00
 * <p>
 * (propagation = Propagation.REQUIRED)
 * 事务的传播行为中，REQUIRED表示，当前service如果有事务，
 * 那么当前service中调用的其他所有service，都不再开启新事务，直接使用当前service的事务
 * 如果当前service没有事务，那么就默认使用第一个调用的service的事务
 */
@Slf4j
@Service
@Transactional
public class GoodsService {

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private SpuDetailMapper detailMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private BrandService brandService;

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
     * 根据条件分页查询商品：spu
     *
     * @param key      搜索条件
     * @param saleable 是否上下架
     * @param page     当前页
     * @param rows     页大小
     * @return 当前页的分页数据
     */
    public PageResult<SpuDTO> findSpuByPage(Integer page, Integer rows, String key, Boolean saleable) {
        // 1.1 提供分页信息
        PageHelper.startPage(page, rows);
        // 1.2 创建一个封装复制条件的对象
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        // 1.3 封装复杂条件
        // if(StringUtils.isNotBlank(key)){
        //     criteria.andLike("name", "%" + key + "%");
        // }
        if (!StringUtils.isBlank(key)) {
            criteria.andLike("name", "%" + key + "%");
        }
        /**
         * 注意mysql数据中tinyint(1)表示布尔值
         * java向改字段传true，则自动保存为1，传false则自动保存为0
         * 该字段如果是0，java可以直接用false获取，非0就是true
         */
        // 2.1 封装是否上下架
        if (saleable != null) {
            criteria.andEqualTo("saleable", saleable);
        }
        // 2.2 数据库查询
        List<Spu> spuList = spuMapper.selectByExample(example);
        // 判空
        if (CollectionUtils.isEmpty(spuList)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        // 3. 封装一个PageHelper的分页对象
        PageInfo<Spu> pageInfo = new PageInfo<>(spuList);
        // 3.1 总页数
        int totalPages = pageInfo.getPages();
        // 3.2 总记录数
        long total = pageInfo.getTotal();
        // 3.3 当前页数据
        List<Spu> list = pageInfo.getList();
        // 3.4 转换DTO:此时，分类和品牌只有Id，还没有他们的名称
        List<SpuDTO> spuDTOS = BeanHelper.copyWithCollection(list, SpuDTO.class);
        // 3.5 把分类和品牌的名称处理
        handlerCategoryNameAndBradName(spuDTOS);
        // 4. 封装分页对象并返回
        return new PageResult<SpuDTO>(
                total,
                totalPages,
                spuDTOS
        );
    }

    /**
     * 把分类的名称和品牌的名称补全
     *
     * @param spuDTOS spu的列表数据
     */
    private void handlerCategoryNameAndBradName(List<SpuDTO> spuDTOS) {
        for (SpuDTO spuDTO : spuDTOS) {
            // 品牌名称
            Long brandId = spuDTO.getBrandId();
            BrandDTO brandDTO = brandService.findBrandById(brandId);
            spuDTO.setBrandName(brandDTO.getName());
            // 分类名称
            List<Long> categoryIds = spuDTO.getCategoryIds();
            List<CategoryDTO> categoryDTOList = categoryService.findCategoryListByIds(categoryIds);
            String categoryNames = categoryDTOList.stream()
                    .map(CategoryDTO::getName)
                    .collect(Collectors.joining("/"));
            spuDTO.setCategoryName(categoryNames);
        }
    }

    /**
     * 保存商品
     *
     * @param spuDTO
     * @return
     */
    public void saveGoods(SpuDTO spuDTO) {
        try {
            // 获取Spu对象
            Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
            // 设置商品为下架
            spu.setSaleable(false);
            // 保存Spu
            spuMapper.insertSelective(spu);
            // 获取SpuDetail对象
            SpuDetail spuDetail = spuDTO.getSpuDetail();
            // 给主键赋值
            spuDetail.setSpuId(spu.getId());
            // 保存SpuDetail
            detailMapper.insertSelective(spuDetail);

            // 获取sku集合
            List<Sku> skuList = spuDTO.getSkus();
            // 对sku集合中的元素进行处理
            skuList.forEach(sku -> {
                // 给外键设置值
                sku.setSpuId(spu.getId());
                // 给保存时间设置值
                sku.setCreateTime(new Date());
                // 给更新时间设置值
                sku.setUpdateTime(new Date());
            });
            // 保存sku集合
            skuMapper.insertList(skuList);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
    }

    /**
         * 商品上下架操作
     *
     * @param id       商品id
     * @param saleable 是否上下架： ture=上架，false=下架
     * @return 在修改spu的上下架状态时，无需修改sku的enable属性，sku的enable属性只与当前sku的库存量有关，如果库存大于0，就是有效，否则是无效。
     * updateByPrimaryKeySelective ： 根据主键更新对象中非空的字段
     * update tb_spu set saleable - ? where id = ？
     */
    public void updateSaleable(Long id, Boolean saleable) {
        //凡是根据id做条件的修改都是简单修改，否则就是要用Example对象来封装复杂修改
        try {
            Spu record = new Spu();
            record.setId(id);
            record.setSaleable(saleable);
            spuMapper.updateByPrimaryKeySelective(record);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
        // 发送Mq消息，生成或者删除，商品的索引和静态页
        amqpTemplate.convertAndSend(
                MQConstants.Exchange.ITEM_EXCHANGE_NAME, // 交换机的名称
                (saleable ? MQConstants.RoutingKey.ITEM_UP_KEY : MQConstants.RoutingKey.ITEM_DOWN_KEY), // 路由Key
                id // 发送商品的id
        );
    }

    /**
     * 根据Spuld查询Sku对象集合
     *
     * @param id
     * @return
     */
    public List<Sku> findSkusBySpuId(Long spuId) {
        // 拼装条件查询
        Sku sku = new Sku();
        sku.setSpuId(spuId);
        List<Sku> skuList = skuMapper.select(sku);
        // 判空
        if (CollectionUtils.isEmpty(skuList)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return skuList;
    }

    /**
     * 根据SpuId查询SpuDetail对象
     *
     * @param id
     * @return
     */
    public SpuDetail findSpuDetailById(Long id) {
        SpuDetail spuDetail = detailMapper.selectByPrimaryKey(id);
        if (spuDetail == null) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return spuDetail;
    }


    /**
     * 根据id查询spu：  返回的对象包含了SpuDetail和Sku列表
     *
     * @param spuId
     * @return
     */
    public SpuDTO findSpuById(Long spuId) {
        // 1、先查询spu
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        if (spu == null) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        // 2、转成DTO
        SpuDTO spuDTO = BeanHelper.copyProperties(spu, SpuDTO.class);
        // 3、查询SpuDetail
        SpuDetail spuDetail = findSpuDetailById(spuId);
        spuDTO.setSpuDetail(spuDetail);
        // 4、查询Sku列表
        List<Sku> skuList = findSkusBySpuId(spuId);
        spuDTO.setSkus(skuList);
        // 5、返回
        return spuDTO;
    }
}
